/*!
\file   op_odbc_inst.cpp
\author coanor <coanor1126@hotmail.com>
\brief  This program will aid installers when installing/uninstalling
OPODBC.

This program can; register/deregister a OPodbc driver and create
a sample dsn. The key thing here is that it does this with 
cross-platform code - thanks to the ODBC installer API. This is
most useful to those creating installers (apps using OPodbc or 
OPodbc itself).

For example; this program is used in the postinstall script
of the OPODBC for Mac OS X installer package. 
*/

/*!
\note   This program uses odbcs.h in similar fashion to the *driver*
but does not use it the way the setup library does. 
*/

#include <stdio.h>

#ifdef WIN32
#	include <windows.h>
#else
#	include <ltdl.h>
#endif

#include "op_odbc_setup.h"

#ifdef USE_IODBC
#	include <iodbcinst.h>
#else
#	include <odbcinst.h>
#endif

typedef BOOL (INSTAPI *config_dsn_functor)(HWND, WORD, LPCSTR, LPCSTR);

char *syntax_desc = 
"\n" \
"+----------------------------------------------------------+\n" \
"|                          opodbc3i                        |\n" \
"+----------------------------------------------------------+\n" \
"\n" \
"  Use this program to manage ODBC system information from the\n" \
"  command-line. This is particularly useful for installation \n" \
"  processes. It is also useful for testing the abstraction/\n" \
"  portability of ODBC system information provided by the  \n" \
"  odbcs library.\n" \
"\n" \
"+----------------------------------------------------------+\n" \
"|                         U S A G E                        |\n" \
"+----------------------------------------------------------+\n" \
"\n" \
"  $ op_odbc_drv.exe <action> <object> <option>\n" \
"\n" \
"+----------------------------------------------------------+\n" \
"|                        A C T I O N                       |\n" \
"+----------------------------------------------------------+\n" \
"\n" \
"  q    Query\n" \
"  a    Add\n" \
"  e    Edit\n" \
"  r    Remove\n" \
"\n" \
"+----------------------------------------------------------+\n" \
"|                        O B J E C T                       |\n" \
"+----------------------------------------------------------+\n" \
"\n" \
"  d    Driver.\n" \
"  n    Driver or data source name. String follows this.\n" \
"  s    User & system data source(s).\n" \
"  su   User data source(s).\n" \
"  ss   System data source(s).\n" \
"\n" \
"+----------------------------------------------------------+\n" \
"|                        O P T I O N                       |\n" \
"+----------------------------------------------------------+\n" \
"\n" \
"  t    Attribute string. String of semi delim key=value pairs\n" \
"       follows this.\n" \
"  w    Window handle. Numeric follows this. 0 to disable GUI \n" 
"       prompts, 1 to fake a window handle (default).\n" \
"\n" \
"+----------------------------------------------------------+\n" \
"|                        S Y N T A X                       |\n" \
"+----------------------------------------------------------+\n" \
"\n" \
"  q d|s|su|ss [n] [w]\n" \
"  a d|s|su|ss t [w]\n" \
"  e s|su|ss t [w]\n" \
"  r d|s|su|ss n [w]\n" \
"\n" \
"+----------------------------------------------------------+\n" \
"|                       E X A M P L E S                    |\n" \
"+----------------------------------------------------------+\n" \
"\n" \
"  Register the OP_ODBC.dll in Windows;\n" \
"	 $ op_odbc_drv.exe -a -d -t\"op_odbc_drv;\n" \
"                       Driver=C:\\WINDOWS\\system32\\op_odbc.dll;\n" \
"                       Setup=C:\\WINDOWS\\system32\\op_odbc_drv.exe\"\n" \
"  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n" \
"  Register a driver in Unix/Linux(Have not support yet);\n" \
"    $ op_odbc_drv.exe -a -d -t\"op_odbc_drv;\n" \
"                      Driver=/usr/lib/libopodbc.so;\n" \
"                      Setup=/usr/lib/op_odbc_drv\"\n" \
"  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n" \
"  Create a user data source name;\n" \
"    $ op_odbc_drv.exe -a -su -t\"DSN=op_dsn;\n" \
"                       Driver=op_odbc_drv;\n" \
"                       Server=localhost;\n" \
"                       UID=pharvey\"\n" \
"  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n" \
"  List all data source names;\n" \
"    $ op_odbc_drv.exe -q -s\n" \
"  List data source attributes;\n" \
"    $ op_odbc_drv.exe -q -s -n\"op_odbc_drv\"\n" 
;

/*
Register Driver for Various Platforms

XP

$ OP_ODBC_INST.exe -a -d -t"openPlant ODBC 3.0 Driver;Driver=opodbc3.dll;Setup=OPodbc3S.dll"

OR

$ OP_ODBC_INST.exe -a -d -t"openPlant ODBC 3.0 Driver;Driver=OPodbc3.dll;Setup=OPodbc3.dll"

Mac OS X

At least some of the functions dealing with the
odbc system information are case sensitive
(contrary to the ODBC spec.). They appear to like
leading caps.

$ OP_ODBC_INST.exe -a -d -t"openPlant ODBC 3.51 Driver;Driver=/usr/lib/libOPodbc3.so;Setup=/usr/lib/libOPodbc3S.dylib"

UNIX/Linux

$ OP_ODBC_INST.exe -a -d -t"openPlant ODBC 3.51 Driver;DRIVER=/usr/lib/libOPodbc3.so;SETUP=/usr/lib/libOPodbc3S.so"
*/

char    g_action         = '\0';
char    g_object         = '\0';
char    g_object_sub      = '\0';
long    g_wnd            = 1;
char *  g_name         = NULL;
char *  g_attributes   = NULL;

char    g_buffer[32000];
int     g_n_buffer = 32000;

/*!
\brief  Prints any installer errors sitting in the installers
error queue.

General purpose error dump function for installer 
errors. Hopefully; it provides useful information about
any failed installer call.

\note   Does not try to process all 8 possible records from the queue.
*/            
void do_print_install_error(char *pszFile, int nLine)
{
	WORD      record = 1;
	DWORD     err;
	char      sz_error[SQL_MAX_MESSAGE_LENGTH];
	RETCODE   ret;

	ret = SQLInstallerError(record, &err, sz_error, SQL_MAX_MESSAGE_LENGTH - 1, 0);
	if (SQL_SUCCEEDED(ret))
		fprintf(stderr, "[%s][%d][ERROR] ODBC Installer error %d: %s\n", pszFile, nLine, (int)err, sz_error);
	else
		fprintf(stderr, "[%s][%d][ERROR] ODBC Installer error (unknown)\n", pszFile, nLine);
}

int do_query();
int do_query_driver();
int do_query_driver_name();
int do_query_data_source();
int do_query_data_source_name(UWORD scope);
int ad_add();
int do_add_driver();
int do_add_data_source();
int do_edit();
int do_edit_driver();
int do_edit_data_source();
int do_remove();
int do_remove_driver();
int do_remove_data_source();
int do_remove_data_source_name();
int do_remove_data_source(WORD nRequest);

int main(int argc, char* argv[])
{
	int arg;
	int ret;

	// For newbie
	if(argc < 2) {
		printf(syntax_desc);
		exit(1);
	}

	// Parse args
	for(arg = 1; arg < argc; ++arg) {
		if(argv[arg][0] == '-') {
			switch (argv[arg][1]) {
				// Actions
			case 'e':
			case 'q':
			case 'a':
			case 'r':
				g_action = argv[arg][1];
				break;
				// Objects
			case 'd':
			case 's':
				g_object = argv[arg][1];
				g_object_sub = argv[arg][2];
				break;
				// Options
			case 'n':
				g_name = &(argv[arg][2]);
				break;
			case 't':
				g_attributes = &(argv[arg][2]);
				break;
			case 'w':
				g_wnd = atoi(&(argv[arg][2]));
				break;
			default: {
					printf(syntax_desc);
					exit(1);
				}
			}
		} else{
			printf(syntax_desc);
			exit(1);
		}		
	}

	// Process args
	switch (g_action) {
	case 'q':
		ret = do_query();
		break;
	case 'a':
		ret = ad_add();
		break;
	case 'e':
		ret = do_edit();
		break;
	case 'r':
		ret = do_remove();
		break;
	default:
		printf("[%s][%d][ERROR] Invalid, or missing, action.\n", __FILE__, __LINE__);
		exit (1);
	}	

	return (!ret);
}

/*!
\brief  Show a list of drivers or data source names.


\note   XP

SQLGetPrivateProfileString() with a NULL 1st arg does not
return anything - ever. To return a list of drivers we can
use SQLGetInstalledDrivers() but no alternative in ODBC
installer for getting a list of data source names.
*/
int do_query()
{
	switch (g_object) {
	case 'd':
		return do_query_driver();
	case 's':
		return do_query_data_source();
	default:
		fprintf(stderr, "[%s][%d][ERROR] Missing or invalid object type specified.\n", __FILE__, __LINE__);
		return 0;
	}
}

int do_query_driver()
{
	char * psz;

	if(g_name)
		return do_query_driver_name();

	if(!odbc_get_driver_names(g_buffer, g_n_buffer)) {
		do_print_install_error(__FILE__, __LINE__);
		fprintf(stderr, "[%s][%d][WARNING] Failed to get driver names. Perhaps none installed?\n", __FILE__, __LINE__);
		return 0;
	}

	psz = g_buffer;
	while (*psz) {
		printf("%s\n", psz);
		psz += strlen(psz) + 1;
	}

	return 1;	
}

int do_query_driver_name()
{
	int ret = 0;
	odbc_driver_t * driver = odbc_alloc_driver();

	if (!odbc_read_driver(driver, g_name, NULL)) {
		fprintf(stderr, "[%s][%d][WARNING] Could not load (%s)\n", __FILE__, __LINE__, g_name);
		goto doQueryDriverNameExit1;
	}

	printf("Name............: %s\n", driver->name);
	printf("DRIVER..........: %s\n", driver->driver_name);
	printf("SETUP...........: %s\n", driver->pszSETUP);

	ret = 1;

doQueryDriverNameExit1:
	odbc_free_driver(driver);

	return ret;
}

int do_query_data_source()
{
	int ret;
	char * psz;

	switch(g_object_sub) {
	case '\0':
		if (g_name)
			return do_query_data_source_name(ODBC_BOTH_DSN);
		else
			ret = odbc_get_data_source_names(g_buffer, g_n_buffer, ODBC_BOTH_DSN);
		break;
	case 'u':
		if (g_name)
			return do_query_data_source_name(ODBC_BOTH_DSN);
		else
			ret = odbc_get_data_source_names(g_buffer, g_n_buffer, ODBC_BOTH_DSN);
		break;
	case 's':
		if (g_name)
			return do_query_data_source_name(ODBC_BOTH_DSN);
		else
			ret = odbc_get_data_source_names(g_buffer, g_n_buffer, ODBC_BOTH_DSN);
		break;
	default:
		fprintf(stderr, "[%s][%d][ERROR] Invalid object type qualifier specified.\n", __FILE__, __LINE__);
		return 0;
	}

	if (!ret) {
		do_print_install_error(__FILE__, __LINE__);
		return 0;
	}

	psz = g_buffer;
	while(*psz) {
		printf("%s\n", psz);
		psz += strlen(psz) + 1;
	}

	return 1;
}

int do_query_data_source_name(UWORD scope)
{
	int ret = 0; 
	odbc_data_source_t *data_source = odbc_alloc_data_source(OPODBCUTIL_DATASOURCE_MODE_DSN_VIEW);

	/* set scope */
	if (!SQLSetConfigMode(scope))
		return FALSE;

	if (!odbc_read_data_source(data_source, g_name)) {
		fprintf(stderr, "[%s][%d][WARNING] Could not load (%s)\n", __FILE__, __LINE__, g_name);
		goto doQueryDataSourceNameExit1;   
	}

	printf("DSN.............: %s\n", data_source->dsn_name);
	printf("DRIVER..........: %s\n", data_source->driver_name);
	printf("Driver file name: %s\n", data_source->drv_file_name);
	printf("DESCRIPTION.....: %s\n", data_source->dsn_desc);
	printf("SERVER..........: %s\n", data_source->srv_name);
	printf("UID.............: %s\n", data_source->user_name);
	printf("PWD.............: %s\n", data_source->passwd);
	printf("DATABASE........: %s\n", data_source->default_db);
	printf("PORT............: %s\n", data_source->srv_port);
	printf("SOCKET..........: %s\n", data_source->sock_name);
	printf("STMT............: %s\n", data_source->stmt);
	printf("OPTION..........: %s\n", data_source->option);

	ret = 1;

doQueryDataSourceNameExit1:
	/* unset scope */
	switch (scope) {
	case ODBC_BOTH_DSN:
		SQLSetConfigMode(ODBC_BOTH_DSN);
		break;
	case ODBC_USER_DSN:
	case ODBC_SYSTEM_DSN:
		SQLSetConfigMode(ODBC_BOTH_DSN);
		break;
	}

	odbc_free_data_source(data_source);

	return ret;
}

int ad_add()
{
	switch (g_object) {
	case 'd':
		return do_add_driver();
	case 's':
		return do_add_data_source();
	default:
		fprintf(stderr, "[%s][%d][ERROR] Missing or invalid object type specified.\n", __FILE__, __LINE__);
		return 0;
	}
}

/*!
\brief  Register the driver (or just increase usage count).

\note   XP

On MS Windows (XP for example) the driver is registered in two places; 
1) \windows\system32\odbcinst.ini
2) registry
Fortunately the installer calls will ensure they are both updated.

All ODBC drivers *should* be installed in the standard location (\windows\system32) and this call
reflects this as no path is given for the driver file.

\note   Mac OS X

On Mac OS X there are many odbcinst.ini files - each account has one in ~/Library/ODBC and there
is a system wide one in /Library/ODBC.

There are at least two notable complicating factors;
- the files are read-ony for average user so one should use sudo when doing this
- the files do not exist until someone manually creates entries using the ODBC Administrator AND
they are not created by iodbc installer lib when we execute this code (we get error)

ODBC spec says that "Driver" should NOT include path 
but path seems needed for iodbc. The implication is that
the driver *must* be installed in /usr/lib for this to work.

Usage Count is not returned on Mac OS X and returned location does not seem to reflect reality.

\note   Linux/UNIX

ODBC spec says that "Driver" should NOT include path 
but path seems needed for unixODBC. The implication is that
the driver *must* be installed in /usr/lib for this to work.

Location returned does not seem to reflect reality.
*/
int do_add_driver()
{
	char  *psz_driverInfo = NULL;
	char  szLoc[FILENAME_MAX];
	WORD  nLocLen;
	DWORD nUsageCount;
	int   nChar;

	if (!g_attributes) {
		fprintf(stderr, "[%s][%d][ERROR] Please provide driver attributes.\n", __FILE__, __LINE__);
		return 0;
	}

	switch (g_object_sub) {
	case 'u':
		if (!SQLSetConfigMode(ODBC_USER_DSN))
			return FALSE;
	case 's':
		if (!SQLSetConfigMode(ODBC_SYSTEM_DSN))
			return FALSE;
	}

	/*
	Create a oopy of g_attributes where the ';' are replaced
	with '\0' and ensure that at least 2 '\0' are at the end.
	SQLInstallDriverEx() needs it formatted this way.
	*/
	psz_driverInfo = (char *)malloc(strlen(g_attributes) + 2);

	for (nChar = 0; g_attributes[nChar]; nChar++) {
		if (g_attributes[nChar] == ';')
			psz_driverInfo[nChar] = '\0';
		else
			psz_driverInfo[nChar] = g_attributes[nChar];
	}

	psz_driverInfo[nChar++]  = '\0';
	psz_driverInfo[nChar]    = '\0';

	/*
	Call ODBC installer to do the work.
	*/    
	if (!SQLInstallDriverEx(psz_driverInfo, 
							0, 
							szLoc, 
							FILENAME_MAX, 
							&nLocLen, 
							ODBC_INSTALL_COMPLETE, 
							&nUsageCount)) {
		do_print_install_error(__FILE__, __LINE__);
		fprintf(stderr, "[%s][%d][ERROR] Failed to register driver\n", __FILE__, __LINE__);
		free(psz_driverInfo);
		return 0;
	}

	printf("[%s][%d][INFO] Driver registered. Usage count is %d. Location \"%s\" \n", 
		__FILE__, 
		__LINE__, 
		(int)nUsageCount, szLoc);

	free(psz_driverInfo);

	return 1;
}

int do_add_data_source()
{
	int ret = 1;

	switch (g_object_sub) {
	case '\0':
		return do_remove_data_source(ODBC_ADD_DSN);
	case 'u':
		if (!SQLSetConfigMode((UWORD)ODBC_USER_DSN)) {
			do_print_install_error(__FILE__, __LINE__);
			fprintf(stderr, "[%s][%d][ERROR] Failed to set config mode.\n", __FILE__, __LINE__);
			return 0;
		}
		ret = do_remove_data_source(ODBC_ADD_DSN);
		SQLSetConfigMode((UWORD)ODBC_BOTH_DSN);
		break;
	case 's':
		if (!SQLSetConfigMode((UWORD)ODBC_SYSTEM_DSN)) {
			do_print_install_error(__FILE__, __LINE__);
			fprintf(stderr, "[%s][%d][ERROR] Failed to set config mode.\n", __FILE__, __LINE__);
			return 0;
		}
		ret = do_remove_data_source(ODBC_ADD_DSN);
		SQLSetConfigMode((UWORD)ODBC_BOTH_DSN);
		break;
	default:
		fprintf(stderr, "[%s][%d][ERROR] Missing or invalid object sub-type specified.\n", __FILE__, __LINE__);
		return 0;
	}

	return ret;
}

int do_edit()
{
	switch (g_object) {
	case 'd':
		return do_edit_driver();
	case 's':
		return do_edit_data_source();
	default:
		fprintf(stderr, "[%s][%d][ERROR] Missing or invalid object type specified.\n", __FILE__, __LINE__);
		return 0;
	}
}

int do_edit_driver()
{
	return 1;
}

int do_edit_data_source()
{
	int ret = 1;

	switch (g_object_sub) {
	case '\0':
		return do_remove_data_source(ODBC_CONFIG_DSN);
	case 'u':
		if (!SQLSetConfigMode(ODBC_USER_DSN)) {
			do_print_install_error(__FILE__, __LINE__);
			fprintf(stderr, 
				"[%s][%d][ERROR] Failed to set config mode.\n", 
				__FILE__, 
				__LINE__);
			return 0;
		}
		ret = do_remove_data_source(ODBC_CONFIG_DSN);
		SQLSetConfigMode(ODBC_BOTH_DSN);
		break;
	case 's':
		if (!SQLSetConfigMode(ODBC_SYSTEM_DSN)) {
			do_print_install_error(__FILE__, __LINE__);
			fprintf(stderr, 
				"[%s][%d][ERROR] Failed to set config mode.\n", 
				__FILE__,
				__LINE__);
			return 0;
		}
		ret = do_remove_data_source(ODBC_CONFIG_DSN);
		SQLSetConfigMode(ODBC_BOTH_DSN);
		break;
	default:
		fprintf(stderr,
			"[%s][%d][ERROR] Invalid object sub-type specified.\n", 
			__FILE__, 
			__LINE__);
		return 0;
	}

	return ret;
}

int do_remove()
{
	switch (g_object)
	{
	case 'd':
		return do_remove_driver();
	case 's':
		return do_remove_data_source();
	default:
		fprintf(stderr, 
			"[%s][%d][ERROR] Missing or invalid object type specified.\n",
			__FILE__,
			__LINE__);
		return 0;
	}
}

/*!
\brief  Deregister the driver.

Simply removes driver from the list of known ODBC 
drivers - does not remove any files.
*/
int do_remove_driver()
{
	DWORD nUsageCount;
	BOOL  bRemoveDSNs = FALSE;

	if (!g_name) {
		fprintf(stderr, "[%s][%d][ERROR] Please provide driver name.\n", __FILE__, __LINE__);
		return 0;
	}

	if (!SQLRemoveDriver(g_name, bRemoveDSNs, &nUsageCount)) {
		do_print_install_error(__FILE__, __LINE__);
		fprintf(stderr, 
			"[%s][%d][ERROR] Failed to deregister driver.\n", 
			__FILE__, 
			__LINE__);
		return 0;
	}

	printf("[%s][%d][INFO] Driver deregistered. Usage count is %d.\n", 
		__FILE__, 
		__LINE__, 
		(int)nUsageCount);

	return 1;
}

int do_remove_data_source()
{
	int ret = 1;

	switch (g_object_sub) {
	case '\0':
		ret = do_remove_data_source_name();
	case 'u':
		if (!SQLSetConfigMode(ODBC_USER_DSN)) {
			do_print_install_error(__FILE__, __LINE__);
			fprintf(stderr, 
				"[%s][%d][ERROR] Failed to set config mode.\n", 
				__FILE__, 
				__LINE__);
			return 0;
		}
		ret = do_remove_data_source_name();
		SQLSetConfigMode(ODBC_BOTH_DSN);
	case 's':
		if (!SQLSetConfigMode(ODBC_SYSTEM_DSN)) {
			do_print_install_error(__FILE__, __LINE__);
			fprintf(stderr, 
				"[%s][%d][ERROR] Failed to set config mode.\n", 
				__FILE__, 
				__LINE__);
			return 0;
		}
		ret = do_remove_data_source_name();
		SQLSetConfigMode(ODBC_BOTH_DSN);
		break;
	default:
		fprintf(stderr,
			"[%s][%d][ERROR] Missing or invalid object sub-type specified.\n",
			__FILE__, 
			__LINE__);
		return 0;
	}

	return ret;
}

/*!
\brief  Remove data source name from ODBC system information.
*/
int do_remove_data_source_name()
{
	if (SQLRemoveDSNFromIni(g_name) == FALSE)
	{
		do_print_install_error(__FILE__, __LINE__);
		fprintf(stderr, "[%s][%d][ERROR] Request failed.\n", __FILE__, __LINE__);
		return 0;
	}

	return 1;
}

#if defined(_DIRECT_)
/*!
\brief  Call drivers ConfigDSN when we are linked directly to driver.

\note   This needs to be updated to reflect recent code changes.
*/
int do_remove_data_source(WORD nRequest)
{
	char  sz_attributes[nDataSourceNameAttributesLength + strlen(pszDataSourceName) + 5];

	sprintf(sz_attributes, "DSN=%s", pszDataSourceName);
	memcpy(&(sz_attributes[strlen(sz_attributes) + 1]), 
		pszDataSourceNameAttributes, 
		nDataSourceNameAttributesLength); 

	if (!ConfigDSN((HWND)bGUI /* fake window handle */, nRequest, psz_driverName, sz_attributes)) {
		do_print_install_error(__FILE__, __LINE__);
		return 0;
	}

	return 1;
}
#else
/*!
\brief  Call drivers ConfigDSN without going through the driver-manager.
*/
int do_remove_data_source(WORD nRequest)
{
	int ret = FALSE; 
	odbc_driver_t *driver = odbc_alloc_driver();
	odbc_data_source_t *data_source_given =
	   odbc_alloc_data_source(OPODBCUTIL_DATASOURCE_MODE_DSN_VIEW);
	odbc_data_source_t *data_source =
	   odbc_alloc_data_source(OPODBCUTIL_DATASOURCE_MODE_DSN_VIEW);
	config_dsn_functor pFunc;

#if defined(WIN32)
	HINSTANCE hLib = 0;
#else
	void *hLib = 0;
	void *hLibDrv = 0;
#endif

	char sz_driver[1024];
	char sz_attributes[1024];

	/* sanity check */
	if (!g_attributes) {
		fprintf(stderr, 
			"[%s][%d][ERROR] Please provide an attributes string.\n", 
			__FILE__, 
			__LINE__);
		goto do_config_data_sourc_exit2;   
	}

	/* parse given attribute string */
	if (!odbc_read_data_source_str(data_source_given,
		    OPODBCUTIL_DELIM_SEMI, 
		    g_attributes)) {
	    fprintf(stderr,
		    "[%s][%d][ERROR] Malformed attribute string (%s)\n",
		    __FILE__,
		    __LINE__, 
		    g_attributes);
	    goto do_config_data_sourc_exit2;
	}

	/* check that we have min attributes for request */
	if (nRequest == ODBC_ADD_DSN) {
		if (data_source_given->driver_name) {
			/* pull driver from attributes as we can not have it in there for ConfigDSN() */
			sprintf(sz_driver, "%s", data_source_given->driver_name);
			_global_free(data_source_given->driver_name);
			data_source_given->driver_name = NULL;
		} else {
			fprintf(stderr,
			       	"[%s][%d][WARNING] DRIVER attribute not provided - using %s.\n",
			       	__FILE__,
			       	__LINE__,
			       	OPODBCINST_DRIVER_NAME);
			sprintf(sz_driver, "%s", OPODBCINST_DRIVER_NAME);
		}
	} else {
		if (!data_source_given->dsn_name) {
			fprintf(stderr,
			       	"[%s][%d][ERROR] Please provide DSN attribute.\n",
			       	__FILE__,
			       	__LINE__);
			goto do_config_data_sourc_exit2;
		}

		/* get driver name for given dsn */
		if (!odbc_read_data_source(data_source, data_source_given->dsn_name)) 	{
			fprintf(stderr,
			       	"[%s][%d][ERROR] Could not load data source name info (%s)\n", 
				__FILE__,
			       	__LINE__,
			       	data_source_given->dsn_name);
			goto do_config_data_sourc_exit2;
		}

		sprintf(sz_driver, "%s", data_source->driver_name);
	}

	/* get setup library name */
	if (!odbc_read_driver(driver, sz_driver, NULL)) {
		fprintf(stderr, 
			"[%s][%d][ERROR] Could not load driver info (%s)\n",
		       	__FILE__, 
			__LINE__, 
			sz_driver);
		goto do_config_data_sourc_exit2;   
	}

	/* create a NULL delimited set of attributes for ConfigDSN() call */
	odbc_write_data_source_str(data_source_given, 
		OPODBCUTIL_DELIM_NULL,
	       	sz_attributes, 
		sizeof(sz_attributes) - 1);  

#if defined(WIN32)
	/* load it */
	DWORD dw;
	hLib = LoadLibrary(driver->pszSETUP);
	//int i = GetLastError();
	if (!hLib) {
		//doPrintLastErrorString();
		fprintf(stderr,
		       	"[%s][%d][ERROR] Could not load driver setup library (%s).\n",
		       	__FILE__,
		       	__LINE__, 
			    driver->pszSETUP);
		goto do_config_data_sourc_exit2;   
	}

	/* lookup ConfigDSN */
	pFunc = (config_dsn_functor) GetProcAddress(hLib, "ConfigDSN");
	if (!pFunc) {
		//doPrintLastErrorString();
		fprintf(stderr,
		       	"[%s][%d][ERROR] Could not find ConfigDSN in (%s).\n",
		       	__FILE__, 
			__LINE__, 
			driver->pszSETUP);
		goto do_config_data_source_exit1;
	}
#else
	/* load it */
	lt_dlinit();

	if (!(hLibDrv = lt_dlopen(driver->driver_name))) {
		fprintf(stderr,
		       	"[%s][%d][ERROR] Could not load driver library (%s). Error is %s\n",
		       	__FILE__, 
			__LINE__,
		       	driver->driver_name,
		       	lt_dlerror());
		goto do_config_data_sourc_exit2;   
	}

	if (!(hLib = lt_dlopen(driver->pszSETUP))) {
		fprintf(stderr,
		       	"[%s][%d][ERROR] Could not load driver setup library (%s). Error is %s\n", 
			__FILE__,
		       	__LINE__, 
			driver->pszSETUP,
		       	lt_dlerror());
		goto do_config_data_sourc_exit2;   
	}

	/* lookup ConfigDSN */
	pFunc = (config_dsn_functor) lt_dlsym(hLib, "ConfigDSN");
	if (!pFunc) {
	    fprintf(stderr,
		    "[%s][%d][ERROR] Could not find ConfigDSN in (%s). Error is %s\n", 
		    __FILE__, 
		    __LINE__,
		    driver->pszSETUP,
		    lt_dlerror());
	    goto do_config_data_source_exit1;   
	}
#endif

	/* make call */
	/*!
	\note

	A fake window handle seems to work for platforms other than Mac OS X :) It will not be
	used as a window handle - just as a flag to get the GUI.
	*/    
	if (!pFunc((HWND)g_wnd /* fake window handle */, nRequest, sz_driver, sz_attributes))
	{
		do_print_install_error(__FILE__, __LINE__);
		goto do_config_data_source_exit1;
	}

	ret = 1;

do_config_data_source_exit1:
#if defined(WIN32)
	FreeLibrary(hLib);
#else
	lt_dlclose(hLibDrv);
	lt_dlclose(hLib);
#endif

	do_config_data_sourc_exit2:
	odbc_free_data_source(data_source);
	odbc_free_data_source(data_source_given);
	odbc_free_driver(driver);

	return ret;
}
#endif
